#include <stdlib.h>
#include "ExeContext.h"
#include "ExeContextRepository.h"
#include "../../List.h"

ECTXTEntry*  ECTXTRNew(){
    ECTXTEntry* New;
    
    New = calloc(sizeof(ECTXTEntry), 1);  
    New->Data = NULL;
    New->Children = calloc(sizeof(ECTXTEntry*), 16);
    New->ContextList = LST_New(NULL);
    
    return New;
}

static inline int hash(uint64_t Key){
    return (Key & 0xF);
}

static void ECTXTRInsert_(ECTXTEntry* Entry, uint64_t ContextHash, ExeContext* Data, uint32_t Depth){
    if(Depth == 16){
        Entry->Data = Data;
        return;
    }
    else{
        int pos;
        pos = hash(ContextHash >> (Depth * 4));

        if(!Entry->Children[pos])
            Entry->Children[pos] = ECTXTRNew();

        ECTXTRInsert_(Entry->Children[pos], ContextHash, Data, Depth+1);
    }
}

void ECTXTRInsert(ECTXTEntry *EntryPoints, ExeContext* Context){
    ECTXTRInsert_(EntryPoints, Context->ECXContextHash, Context, 0);
    LST_Push(EntryPoints->ContextList,Context);
}

static ExeContext* ECTXTRSearch_(ECTXTEntry* Entry, uint64_t ContextHash, uint32_t Depth){
    if(Depth == 16){
        return Entry->Data;
    }
    else{
        int pos;
        pos = hash(ContextHash >> (Depth * 4));

        if(!Entry->Children[pos]){
            return NULL;
        }
        return ECTXTRSearch_(Entry->Children[pos], ContextHash, Depth+1);
    }
}

ExeContext* ECTXTRSearch(ECTXTEntry *Entry, ExeContext* Context){
    return ECTXTRSearch_(Entry, Context->ECXContextHash, 0);
}

/*void BMCEPDump_(ECTXTEntry *Entry, uint16_t Data, uint32_t Depth, uint32_t Flags , void (*fn)(void*)){
    uint32_t i;
    
    if(Depth == 4){
        if(Flags & 0x01){
            if(Entry->Data){
                printf("Addr: %04X\n", Data);
            }
        }
        else if(Flags & 0x02){
            if(Entry->Data){
                fn((void*) &Data);
            }
        }
        return;
    }
    else{
        for(i = 0; i < 16; i++){
            if(Entry->Children[i]){
                BMCEPDump_(Entry->Children[i], Data | (i << (Depth * 4)), Depth + 1, Flags, fn);
            }
        }
    }
}

void BMCEPDump(BMC *Block){
    BMCEPDump_(Block->EntryPoints, 0, 0, 1, NULL);
}

void BMCEPExecute(BMC *Block, void (*fn)(void*)){
    BMCEPDump_(Block->EntryPoints, 0, 0, 2, fn);
}*/

